/**
 *  Serponix is an arcade game in focus to multiplayer based on the classic game Snake.
 *  Copyright (C) 2010 - 2011  Daniel Vala
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License,
 *  or  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.

 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  If you have any question do not hesitate to contact author
 *  on e-mail address: danielvala42@gmail.com
 */
package com.serponix.gui;

import com.serponix.game.Consts;
import com.serponix.game.GameModeEnum;
import com.serponix.game.GameParameters;
import com.serponix.game.Mapa;
import com.serponix.game.Player;
import com.serponix.game.score.HighSkore;
import com.serponix.net.Client;
import com.serponix.net.Server;
import com.serponix.net.UDPClient;
import com.serponix.settings.GameSettings;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;

/**
 * Hlavní menu hry obsahuje tlačítka pro založení nové hry, připojení se ke hře,
 * spuštění record módu, zobrazení high-skore, vytvoření mapy, nastavení hry,
 * nápovědy a konec hry.
 *
 * @author Daniel Vala
 */
public class Menu extends JPanel {

	public final String HEADLINE = Consts.NAME_OF_APPLICATION;
	public final String COPYRIGHT = Consts.NAME_OF_APPLICATION + " Copyright (C) 2010  Daniel Vála " + Consts.WEBSITES;
	public final String VERSION;
	private JButton newGame, join, searchGames, tryRecord, showScore, createMap, settings, help, about, exit;
	private JPanel buttonPanel;
	private Box verticalBox;
	private WindowInterface okno;
	private GameParameters gameParameters;
	private JLabel headlineLabel;
	private Menu menu;

	/**
	 * Vytvoří panel hlavního menu.
	 *
	 * @param okno Okno, ve kterém se menu vytvoří.
	 */
	public Menu(WindowInterface okno) {
		menu = this;
		this.okno = okno;

		// read version from manifest file in case of jar
		// we write development for running from IDE
		String version = getClass().getPackage().getSpecificationVersion();
		if (version != null) {
			VERSION = version;
		} else {
			VERSION = "development";
		}

		setLayout(new BorderLayout());

		////// TVORBA PANELŮ //////
		buttonPanel = new JPanel();
		verticalBox = Box.createVerticalBox();

		///////////////////////////
		headlineLabel = new JLabel(HEADLINE);
		headlineLabel.setFont(new Font("font1", Font.BOLD, 50));
		headlineLabel.setAlignmentX(0.5f);

		newGame = new JButton("Založit novou hru");
		join = new JButton("Připojit se ke hře");
		searchGames = new JButton("Vyhledat dostupné hry");
		tryRecord = new JButton("Zkusit rekord");
		showScore = new JButton("Ukázat high-skore");
		createMap = new JButton("Vytvořit mapu");
		settings = new JButton("Nastavení hry");
		help = new JButton("Nápověda");
		about = new JButton("O hře");
		exit = new JButton("Konec hry");

		JPanel paticka = new JPanel(new BorderLayout());
		paticka.add(new JLabel(COPYRIGHT), BorderLayout.WEST);
		paticka.add(new JLabel("verze: " + VERSION), BorderLayout.EAST);

		// nastavení velikosti všech tlačítek
		newGame.setMinimumSize(new Dimension(170, 26));
		newGame.setMaximumSize(new Dimension(170, 26));
		newGame.setAlignmentX(0.5f);

		join.setMinimumSize(new Dimension(170, 26));
		join.setMaximumSize(new Dimension(170, 26));
		join.setAlignmentX(0.5f);

		searchGames.setMinimumSize(new Dimension(170, 26));
		searchGames.setMaximumSize(new Dimension(170, 26));
		searchGames.setAlignmentX(0.5f);

		tryRecord.setMinimumSize(new Dimension(170, 26));
		tryRecord.setMaximumSize(new Dimension(170, 26));
		tryRecord.setAlignmentX(0.5f);

		showScore.setMinimumSize(new Dimension(170, 26));
		showScore.setMaximumSize(new Dimension(170, 26));
		showScore.setAlignmentX(0.5f);

		createMap.setMinimumSize(new Dimension(170, 26));
		createMap.setMaximumSize(new Dimension(170, 26));
		createMap.setAlignmentX(0.5f);

		settings.setMinimumSize(new Dimension(170, 26));
		settings.setMaximumSize(new Dimension(170, 26));
		settings.setAlignmentX(0.5f);

		help.setMinimumSize(new Dimension(170, 26));
		help.setMaximumSize(new Dimension(170, 26));
		help.setAlignmentX(0.5f);

		about.setMinimumSize(new Dimension(170, 26));
		about.setMaximumSize(new Dimension(170, 26));
		about.setAlignmentX(0.5f);

		exit.setMinimumSize(new Dimension(170, 26));
		exit.setMaximumSize(new Dimension(170, 26));
		exit.setAlignmentX(0.5f);

		verticalBox.add(headlineLabel);
		verticalBox.add(newGame);
		verticalBox.add(join);
		//   verticalBox.add(vyhledatHry);
		verticalBox.add(tryRecord);
		verticalBox.add(showScore);
		verticalBox.add(createMap);
		verticalBox.add(settings);
		verticalBox.add(help);
		verticalBox.add(about);
		verticalBox.add(exit);

		String[] typy = new String[23];

		// útočné typy
		typy[0] = "Pokud chcete protivníka co nejrychleji zkrátit, bývá nejlepší taktikou „pověsit“ se mu za ocas a střílet. <br /> " + "Vzhledem k tomu, že střela hned zasáhne cíl, máte k dispozici ihned plný zásobník.";
		typy[1] = "Dvakrát měř, jednou řež. I zde platí toto přísloví. Pokud dobře zamíříte a vystřelíte, až když jste si jisti zásahem, budete mít k dispozici další střelu dříve. <br /> " + "Pokud poprvé minete, pravděpodobně už další šanci mít nebudete a protivník zmizí z dosahu.";
		typy[2] = "Pokud jste při duelu výrazně větší než protivník a vlastníte tank, využívejte ho co nejvíce.";
		typy[3] = "Pokud chcete protivníka zabít na jednu ránu, jeďte přímo proti němu a vystřelte mu do hlavy raketu. <br />";
		typy[4] = "Jestliže se Vám podaří získat vzácnou vlastnost duch, vjeďte do nepřátelského hada a střílejte. Zásobníky budete mít stále plné. <br />" + " Pokud takto střelíte raketu, výrazně ho tím poškodíte.";

		// obranné typy
		typy[5] = "Když ocas nepřátelského hada bliká, blíží se k vítězství. Proto než sežere další jídlo, pokuste se mu ubrat alespoň 3 články. <br />" + "Právě tolik je totiž standardní růst hada po sežrání jídla.";
		typy[6] = "Nepouštějte se do souboje na život a na smrt s delším hadem, pokud si opravdu nevěříte.";
		typy[7] = "Pokud se budete vlnit, jako se správný had vlnit má, snížíte tak šanci, že utržíte ránu. <br />" + " Nepřátelský had se netrefí tak snadno, a jakmile mine, potrvá, než se mu zásobník opět nabije.";
		typy[8] = "Pokud vlastníte tank, snažte se sežrat jídlo i za cenu využití tanku k resetování jiného hada. Sice tím přijdete o jeden článek, <br />" + "stejně tak ale o něj přijde i soupeř. Vy navíc sežráním jídla získáte minimálně 3 další články.";
		typy[9] = "Pokud jste příliš malí, zaměřte se raději na sebírání jídla, než bonusů. Bonus pro Vás nemusí být vždy přínosem a riskujete, že Vás protihráči zabijí.";
		typy[10] = "Jestliže jste pod nepřátelskou palbou a cítíte, že by Vás nepřítel mohl zabít, raději nabourejte do stěny mapy nebo do sebe. <br />" + "Ztratíte tím sice jeden článek, ale většinou se objevíte dál od protivníka.";
		typy[11] = "Pokud se jako dlouhý had zamotáte, bývá někdy výhodné střelit sám sebe a tím se zachránit od nárazu do sebe.";
		typy[12] = "Když už se Váš život chýlí ke konci, vyhněte se všem hadům a někde tiše vyčkávejte na jídlo.";
		typy[13] = "Raketu si šetřete na horší časy.";
		typy[14] = "Většinou bývá lepší se \"starat o sebe\" a sbírat jídlo, než bojovat s ostatními";
		// další typy
		typy[15] = "Zvolte menší rychlost hry pro více strategickou hru. Naopak větší rychlost hry pro více reflexní hru.";
		typy[16] = "Pokud vlastníte lopatu, snažte se ji využít pro co nejkratší cestu za jídlem či bonusem. Díky svobodě pohybu skrz krajní stěny mapy <br />" + " získáte drahocenný náskok před protivníkem a tím seberete jídlo nebo bonus jako první.";
		typy[17] = "Při výrazně zvýšeném růstu díky pilulkám se snažte vyhrát tím způsobem, že dosáhnete jako první potřebné velikosti pro vítězství, než zabíjením ostatních hadů.";
		typy[18] = "Při velkém počtu hráčů bývá prvních pár vteřin hry nejtěžších. Věnujte tedy startu hry zvýšenou koncentraci.";
		typy[19] = "Pokud jste duchem, můžete se pohybovat nejen skrz všechny hady, ale i skrz zdi!";
		typy[20] = "Při kreslení mapy berte v úvahu, že se jídlo může objevit kdekoliv. Proto když se objeví na nedostupném místě, <br />" + "strhne se hra na boj na život a na smrt bez sbírání jídla.";
		typy[21] = "Hlavou zeď neprorazíš. Toto známé přísloví platí, i když vlastníte tank.";
		typy[22] = "Nesbírejte bonus, pokud už jste dost velcí. Zbytečně riskujete prohození s hráčem, který je \"na umření\" a ten poté vyhraje.";

		Random random = new Random();
		int cisloNahodnehoTypu = random.nextInt(typy.length);

		String typTypu;
		if (cisloNahodnehoTypu <= 4) {
			typTypu = "Útočný typ do hry:";
		} else if (cisloNahodnehoTypu <= 14) {
			typTypu = "Obranný typ do hry:";
		} else {
			typTypu = "Typ do hry:";
		}

		JLabel typ = new JLabel("<html>" + typTypu + "<br /><table CELLSPACING=\"0\" CELLPADDING=\"2\" BORDER=\"1\" BORDERCOLOR=\"BLACK\"><TD BGCOLOR=\"#99CCFF\">" + typy[cisloNahodnehoTypu] + "</TD></TR></table></html>");
		JPanel panelTyp = new JPanel();
		panelTyp.add(typ);

		newGame.addActionListener(new HandlerNovaHra());
		join.addActionListener(new HandlerPripojSe());
		searchGames.addActionListener(new HandlerVyhledejHry());
		tryRecord.addActionListener(new HandlerZkusitRekord());
		showScore.addActionListener(new HandlerUkazSkore());
		createMap.addActionListener(new HandlerVytvorMapu());
		settings.addActionListener(new HandlerNastaveni());
		help.addActionListener(new HandlerNapoveda());
		about.addActionListener(new HandlerOHre());
		exit.addActionListener(new HandlerKonec());

		// key listenery pro pohyb menu skrz směrové klávesy
		newGame.addKeyListener(new HandlerPohyb(exit, join));
		join.addKeyListener(new HandlerPohyb(newGame, tryRecord));
		//   vyhledatHry.addKeyListener(new HandlerPohyb(pripojitSe, zkusitRekord));
		tryRecord.addKeyListener(new HandlerPohyb(join, showScore));
		showScore.addKeyListener(new HandlerPohyb(tryRecord, createMap));
		createMap.addKeyListener(new HandlerPohyb(showScore, settings));
		settings.addKeyListener(new HandlerPohyb(createMap, help));
		help.addKeyListener(new HandlerPohyb(settings, about));
		about.addKeyListener(new HandlerPohyb(help, exit));
		exit.addKeyListener(new HandlerPohyb(about, newGame));

		///// PŘIDÁNÍ PANELŮ /////
		buttonPanel.add(verticalBox);

		add(buttonPanel, BorderLayout.NORTH);
		add(panelTyp, BorderLayout.CENTER);
		add(paticka, BorderLayout.SOUTH);
	}

	/**
	 * Nastaví tlačítko jako vybrané tak, že se bude moci stisknout enterem.
	 *
	 * @param button Tlačítko, které bude nastaveno jako vybrané.
	 */
	public void setButtonAsChosen(JButton button) {
		getRootPane().setDefaultButton(button);
		button.requestFocus();
	}

	/**
	 * Set button <b> new game </b> as default.
	 *
	 * @param g
	 */
	@Override
	public void paint(Graphics g) {
		super.paint(g);
		setButtonAsChosen(newGame);
	}

	/**
	 * Po akci zobrazí lobby.
	 */
	private class HandlerNovaHra implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			okno.displayLobbyForServer();
		}
	}

	/**
	 * Po akci zobrazí nastavení.
	 */
	private class HandlerNastaveni implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			okno.zobrazPanelNastaveni();
		}
	}

	/**
	 * Po akci zobrazí nápovědu.
	 */
	private class HandlerNapoveda implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			okno.zobrazPanelNapovedy();
		}
	}

	/**
	 * Po akci zobrazí informace o hře.
	 */
	private class HandlerOHre implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			okno.zobrazPanelOHre();
		}
	}

	/**
	 * Po akci zobrazí high-skore.
	 */
	private class HandlerUkazSkore implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			HighSkore.zobrazSkore();
		}
	}

	/**
	 * Po akci spustí mód rekord.
	 */
	private class HandlerZkusitRekord implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			List<Player> hraci = new ArrayList<Player>();
			hraci.add(new Player(GameSettings.getInstance().getNameOfGivenPlayer(0), Player.Control.KEYBOARD1, null));
			GameParameters parametryHry = new GameParameters(GameModeEnum.RECORD, 40, hraci, new Mapa(), null, (Server) null);
			okno.displayGame(parametryHry);
		}
	}

	/**
	 * Po akci spustí editor map.
	 */
	private class HandlerVytvorMapu implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			gameParameters = new GameParameters(GameModeEnum.EDITOR);
			okno.displayGame(gameParameters);
		}
	}

	/**
	 * Po akci zobrazí dialog na zadání IP adresy.
	 * Zadanou IP adresou se pak pokusí připojit se k serveru.
	 */
	private class HandlerPripojSe implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			GameSettings gameSettings = GameSettings.getInstance();
			String serverIP = JOptionPane.showInputDialog("Zadejte IP adresu", gameSettings.getServerIP());
			if (serverIP == null) {
				return;
			}

			gameSettings.setServerIP(serverIP);
			gameSettings.save();

			Client client = new UDPClient();
			try {
				boolean uspech = client.pripojitSeKServeru(InetAddress.getByName(serverIP));
				if (!uspech) {
					JOptionPane.showMessageDialog(menu, "Server je plný", "Neúspěch", JOptionPane.ERROR_MESSAGE);
					return;
				}
			} catch (SocketTimeoutException ex) {
				JOptionPane.showMessageDialog(menu, "Hra nebyla nalezena \n Vypršel časový limit spojení. \n Ověřte, zda používáte stejný port jako server.", "Chyba", JOptionPane.ERROR_MESSAGE);
				return;
			} catch (UnknownHostException ex) {
				JOptionPane.showMessageDialog(menu, " Hra nebyla nalezena \n IP adresa nebyla rozpoznána. \n Ověřte, zda používáte stejný port jako server.", "Chyba", JOptionPane.ERROR_MESSAGE);
				return;
			} catch (IOException ex) {
				JOptionPane.showMessageDialog(menu, " Hra nebyla nalezena \n Chyba připojení k serveru \n Ověřte, zda používáte stejný port jako server.", "Chyba", JOptionPane.ERROR_MESSAGE);
				return;
			}
			okno.displayLobbyForClient(client);
		}
	}

	/**
	 * Po akci vyhledá založené hry a zobrazí okno s přehledem nalezených her,
	 * do kterých je možno se připojit.
	 */
	private class HandlerVyhledejHry implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			List<InetAddress> nalezeneAdresy = null;
			Client client = new UDPClient();
			try {
				nalezeneAdresy = client.vyhledatHry();
			} catch (IOException ex) {
				System.err.println("Chyba: " + ex.getMessage());
			}
			final List<InetAddress> finalniNalezeneAdresy = nalezeneAdresy;
			final JFrame oknoVyhledanychHer = new JFrame("dostupné hry");
			Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
			Dimension zakladniRozliseni = new Dimension(400, 300);
			oknoVyhledanychHer.setBounds((int) screen.getWidth() / 2 - (int) zakladniRozliseni.getWidth() / 2, 100, (int) zakladniRozliseni.getWidth(), (int) zakladniRozliseni.getHeight());
			oknoVyhledanychHer.setVisible(true);
			JPanel panel = new JPanel();

			if (nalezeneAdresy == null || nalezeneAdresy.isEmpty()) {
				JLabel nenalezeno = new JLabel("žádná hra nebyla nalezena");
				panel.add(nenalezeno);
			}

			ButtonGroup skupinaHer = new ButtonGroup();

			final JRadioButton[] nalezeneHry = new JRadioButton[nalezeneAdresy.size()];
			for (int i = 0; i < nalezeneHry.length; i++) {
				nalezeneHry[i] = new JRadioButton(nalezeneAdresy.get(i).toString());
				skupinaHer.add(nalezeneHry[i]);
				panel.add(nalezeneHry[i]);
			}

			JButton pripojit = new JButton("připojit se");
			pripojit.setSize(getPreferredSize());
			JButton zrusit = new JButton("zrušit");
			zrusit.setSize(getPreferredSize());
			panel.add(pripojit, BorderLayout.SOUTH);
			panel.add(zrusit, BorderLayout.SOUTH);
			oknoVyhledanychHer.add(panel);

			pripojit.addActionListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent e) {
					for (int i = 0; i < nalezeneHry.length; i++) {
						if (nalezeneHry[i].isSelected()) {
							Client client = new UDPClient();
							try {
								client.pripojitSeKServeru(finalniNalezeneAdresy.get(i));
							} catch (SocketTimeoutException ex) {
								JOptionPane.showMessageDialog(null, "Chyba připojení k serveru", "Chyba", JOptionPane.ERROR_MESSAGE);
								return;
							} catch (IOException ex) {
								JOptionPane.showMessageDialog(null, "Chyba připojení k serveru2", "Chyba", JOptionPane.ERROR_MESSAGE);
								return;
							}
							oknoVyhledanychHer.setVisible(false);
							okno.displayLobbyForClient(client);
							break;
						}
					}
				}
			});

			zrusit.addActionListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent e) {
					oknoVyhledanychHer.setVisible(false);
				}
			});
		}
	}

	/**
	 * Po akci ukončí hru.
	 */
	private class HandlerKonec implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			System.exit(0);
		}
	}

	/**
	 * Třída slouží pro pohyb po menu směrovými klávesami.
	 */
	private class HandlerPohyb extends KeyAdapter {

		JButton tlacitkoUp;
		JButton tlacitkoDown;

		public HandlerPohyb(JButton tlacitkoUp, JButton tlacitkoDown) {
			this.tlacitkoUp = tlacitkoUp;
			this.tlacitkoDown = tlacitkoDown;
		}

		@Override
		public void keyPressed(KeyEvent e) {
			switch (e.getKeyCode()) {
				case KeyEvent.VK_UP:
					setButtonAsChosen(tlacitkoUp);
					tlacitkoUp.requestFocus();
					break;
				case KeyEvent.VK_DOWN:
					setButtonAsChosen(tlacitkoDown);
					tlacitkoDown.requestFocus();
					break;
			}
		}
	}
}
